#ifndef __Vector4_hpp__
#define __Vector4_hpp__

#include "Vector3.hpp"

namespace ge {

//----------------------------------------------------------------------------//
// Vector4
//----------------------------------------------------------------------------//

class Vector4
{
 public:

     Vector4( void )
     {
     }

     Vector4( const Vector4& _v ) : x( _v.x ), y( _v.y ), z( _v.z ), w( _v.w )
     {
     }

     Vector4( float _x, float _y, float _z, float _w = 1.0f ) : x( _x ), y( _y ), z( _z ), w( _w )
     {
     }

     Vector4( const float _v[4] ) : x( _v[0] ), y( _v[1] ), z( _v[2] ), w( _v[3] )
     {
     }

     Vector4( float _s ) : x( _s ), y( _s ), z( _s ), w( _s )
     {
     }

     Vector4( const Vector2& _xy, const Vector2& _zw = Vector2( 0.0f, 1.0f ) ) : x( _xy.x ), y( _xy.y ), z( _zw.x ), w( _zw.y )
     {
     }

     Vector4( const Vector3& _v, float _w = 1.0f ) : x( _v.x ), y( _v.y ), z( _v.z ), w( _w )
     {
     }

     float& operator [] ( uint _index )
     {
          return v[_index];         
     }

	    const float& operator [] (  uint _index ) const
     {
          return v[_index];
     }

     float* operator * ( void )
     {
          return v;
     }

     const float* operator * ( void ) const
     {
          return v;
     }

     Vector4& operator = ( const Vector4& _v )
     {
          return Set( _v ); 
     }

     Vector4& operator = ( float _s )
     {
          return Set( _s ); 
     }

     Vector4& operator = ( const Vector3& _v )
     {
          return Set( _v, 1.0 ); 
     }

     bool operator == ( const Vector4& _v ) const
     {
          return x == _v.x && y == _v.y && z == _v.z && w == _v.w;
     }

     bool operator != ( const Vector4& _v ) const
     {
          return !(*this == _v);
     }

     Vector4 operator + ( const Vector4& _v ) const
     {
          return Vector4( x + _v.x, y + _v.y, z + _v.z, w + _v.w );
     }

     Vector4 operator - ( const Vector4& _v ) const
     {
          return Vector4( x - _v.x, y - _v.y, z - _v.z, w - _v.w );
     }

     Vector4 operator * ( const Vector4& _v ) const
     {
          return Vector4( x * _v.x, y * _v.y, z * _v.z, w * _v.w );
     }

     Vector4 operator / ( const Vector4& _v ) const
     {
          return Vector4( x / _v.x, y / _v.y, z / _v.z, w / _v.w );
     }

     friend Vector4 operator + ( float _s, const Vector4& _v )
     {
          return Vector4( _s + _v.x, _s + _v.y, _s + _v.z, _s + _v.w );
     }

     friend Vector4 operator - ( float _s, const Vector4& _v )
     {
          return Vector4( _s - _v.x, _s - _v.y, _s - _v.z, _s - _v.w );
     }

     friend Vector4 operator * ( float _s, const Vector4& _v )
     {
          return Vector4( _s * _v.x, _s * _v.y, _s * _v.z, _s * _v.w );
     }

     friend Vector4 operator / ( float _s, const Vector4& _v )
     {
          return Vector4( _s / _v.x, _s / _v.y, _s / _v.z, _s / _v.w );
     }

     friend Vector4 operator + ( const Vector4& _v, float _s )
     {
          return Vector4( _v.x + _s, _v.y + _s, _v.z + _s, _v.w + _s );
     }

     friend Vector4 operator - ( const Vector4& _v, float _s )
     {
          return Vector4( _v.x - _s, _v.y - _s, _v.z - _s, _v.w - _s );
     }

     Vector4 operator * ( float _s ) const
     {
          return Vector4( x * _s, y * _s, z * _s, w * _s );
     }

     Vector4 operator / ( float _s ) const
     {
          return Vector4( (*this) * ( 1.0f / _s ) );
     }

     Vector4& operator += ( const Vector4& _v )
     {
          x += _v.x;
          y += _v.y;
          z += _v.z;
          w += _v.w;
          return *this; 
     }

     Vector4& operator -= ( const Vector4& _v )
     {
          x -= _v.x;
          y -= _v.y;
          z -= _v.z;
          w -= _v.w;
          return *this; 
     }

     Vector4& operator *= ( const Vector4& _v )
     {
          x *= _v.x;
          y *= _v.y;
          z *= _v.z;
          w *= _v.w;
          return *this; 
     }

     Vector4& operator /= ( const Vector4& _v )
     {
          x /= _v.x;
          y /= _v.y;
          z /= _v.z;
          w /= _v.w;
          return *this; 
     }

     Vector4& operator += ( float _s )
     {
          x += _s;
          y += _s;
          z += _s;
          w += _s;
          return *this; 
     }

     Vector4& operator -= ( float _s )
     {
          x -= _s;
          y -= _s;
          z -= _s;
          w -= _s;
          return *this; 
     }

     Vector4& operator *= ( float _s )
     {
          x *= _s;
          y *= _s;
          z *= _s;
          w *= _s;
          return *this; 
     }

     Vector4& operator /= ( float _s )
     {
          return ( (*this) *= ( 1.0f / _s ) );
     }

     const Vector4& operator + ( void ) const
     {
          return *this;
     }

     Vector4 operator - ( void ) const
     {
          return Vector4( -x, -y, -z, -w );
     }

     float Dot( const Vector4& _v ) const
     {
          return x * _v.x + y * _v.y + z * _v.z + w * _v.w;
     }


     Vector4& Set( float _s )
     {
          x = _s;
          y = _s;
          z = _s;
          w = _s;
          return *this;
     }

     Vector4& Set( float _x, float _y, float _z, float _w = 1.0f )
     {
          x = _x;
          y = _y;
          z = _z;
          w = _w;
          return *this;
     }

     Vector4& FromPtr( const float _v[4] )
     {
          x = _v[0];
          y = _v[1];
          z = _v[2];
          w = _v[3];
          return *this;
     }

     Vector4& Set( const Vector4& _v )
     {
          x = _v.x;
          y = _v.y;
          z = _v.z;
          w = _v.w;
          return *this;
     }

     Vector4& Set( const Vector3& _v, float _w = 1.0f )
     {
          x = _v.x;
          y = _v.y;
          z = _v.z;
          w = _w;
          return *this;
     }

     Vector4& Set( const Vector2& _xy, const Vector2& _zw )
     {
          x = _xy.x;
          y = _xy.y;
          z = _zw.x;
          w = _zw.y;
          return *this;
     }

     Vector4& Set( const Vector2& _v, float _z = 0.0f, float _w = 1.0f )
     {
          x = _v.x;
          y = _v.y;
          z = _z;
          w = _w;
          return *this;
     }

     Vector4& SetZero( void )
     {
          x = 0.0f;
          y = 0.0f;
          z = 0.0f;
          w = 0.0f; // ??
          return *this;
     }

     Vector4& SetIdentity( void )
     {
          x = 1.0f;
          y = 1.0f;
          z = 1.0f;
          w = 1.0f;
          return *this;
     }

     Vector4& SetAxis( uint _axis, float _value )
     {
          v[_axis] = _value;
          return *this;
     }

     static const Vector4 Zero;
     static const Vector4 Identity;

     union
     {
          struct{ float x,y,z,w; };
          float v[4];
     };
};

} // namespace ge

#endif//__Vector4_hpp__
